package cn.bugstack.config;

import cn.bugstack.types.common.Constants;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.BaseCodec;
import org.redisson.client.protocol.Decoder;
import org.redisson.client.protocol.Encoder;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.Config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.Objects;
import java.util.Properties;

/**
 * redis client config 配置文件
 *
 * @author lihh
 */
@Configuration
@EnableConfigurationProperties(RedisClientConfigProperties.class)
public class RedisClientConfig {
  
  // 这是默认的 properties
  private final Properties defaultProperties = new Properties();
  
  @PostConstruct
  public void initHandler() {
    defaultProperties.setProperty(Constants.RedisKey.STR_HOST_KEY, Constants.RedisKey.STR_HOST_VALUE);
    defaultProperties.setProperty(Constants.RedisKey.STR_PORT_KEY, Constants.RedisKey.STR_PORT_VALUE);
  }
  
  /**
   * 有效的 properties check
   *
   * @param properties 非默认的 properties
   * @author lihh
   */
  private void validPropertiesHandler(RedisClientConfigProperties properties) {
    if (Objects.isNull(properties.getHost())) {
      properties.setHost(defaultProperties.getProperty(Constants.RedisKey.STR_HOST_KEY));
    }
    
    if (0 == properties.getPort()) {
      properties.setPort(Integer.parseInt(defaultProperties.getProperty(Constants.RedisKey.STR_PORT_KEY)));
    }
  }
  
  @Bean("redissonClient")
  public RedissonClient redissonClient(RedisClientConfigProperties properties) {
    validPropertiesHandler(properties);
    
    Config config = new Config();
    // 根据需要可以设定编解码器；https://github.com/redisson/redisson/wiki/4.-%E6%95%B0%E6%8D%AE%E5%BA%8F%E5%88%97%E5%8C%96
    config.setCodec(new JsonJacksonCodec());
    
    config.useSingleServer()
        .setAddress("redis://" + properties.getHost() + ":" + properties.getPort())
        .setConnectionPoolSize(properties.getPoolSize())
        .setConnectionMinimumIdleSize(properties.getMinIdleSize())
        .setIdleConnectionTimeout(properties.getIdleTimeout())
        .setConnectTimeout(properties.getConnectTimeout())
        .setRetryAttempts(properties.getRetryAttempts())
        .setRetryInterval(properties.getRetryInterval())
        .setPingConnectionInterval(properties.getPingInterval())
        .setKeepAlive(properties.isKeepAlive())
    ;
    
    return Redisson.create(config);
  }
  
  static class RedisCodec extends BaseCodec {
    
    private final Encoder encoder = in -> {
      ByteBuf out = ByteBufAllocator.DEFAULT.buffer();
      try {
        ByteBufOutputStream os = new ByteBufOutputStream(out);
        JSON.writeJSONString(os, in, SerializerFeature.WriteClassName);
        return os.buffer();
      } catch (IOException e) {
        out.release();
        throw e;
      } catch (Exception e) {
        out.release();
        throw new IOException(e);
      }
    };
    
    private final Decoder<Object> decoder = (buf, state) -> JSON.parseObject(new ByteBufInputStream(buf), Object.class);
    
    @Override
    public Decoder<Object> getValueDecoder() {
      return decoder;
    }
    
    @Override
    public Encoder getValueEncoder() {
      return encoder;
    }
  }
}
